package com.ztf.sm.util;

import org.bouncycastle.asn1.gm.GMNamedCurves;
import org.bouncycastle.asn1.x9.X9ECParameters;
import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
import org.bouncycastle.crypto.InvalidCipherTextException;
import org.bouncycastle.crypto.engines.SM2Engine;
import org.bouncycastle.crypto.generators.ECKeyPairGenerator;
import org.bouncycastle.crypto.params.*;
import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.math.ec.ECPoint;
import org.bouncycastle.util.encoders.Hex;
import sun.misc.BASE64Decoder;

import java.math.BigInteger;
import java.security.*;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;
import org.bouncycastle.jce.spec.ECParameterSpec;
/**
 *
 * @类名称 Sm2Utils.java
 * @类描述 <pre>国密sm2工具类-适用于 js+java后端交互使用</pre>
 * @作者  yw  xingsfdz@163.com
 * @创建时间 2021年4月13日 下午1:52:56
 * @版本 5.0.0
 *
 * @修改记录
 * <pre>
 *     版本                       修改人 		修改日期 		 修改内容描述
 *     ----------------------------------------------
 *     5.0.0 	yw 	2021年4月13日             
 *     ----------------------------------------------
 * </pre>
 */
public class Sm2Utils {

    //流程 通过后台代码生产公私钥对，公钥提供给js端用于数据加密，私钥用于js端传来的密文串解密
    private static ECDomainParameters domainParameters = null;
    private static final String privateKey = "30a13bcae95af7fcc24ef109e2a7d45846c3547f8cc7bffafe0ea967e8cf2730";//生成的私钥 用来解密

    /**
     * 获取椭圆曲线
     */
    static {
        //生成密钥对
        X9ECParameters sm2ECParameters = GMNamedCurves.getByName("sm2p256v1");
        domainParameters = new ECDomainParameters(sm2ECParameters.getCurve(), sm2ECParameters.getG(), sm2ECParameters.getN());
    }


    /**
     *
     * @方法名称 genPUPRkey
     * @功能描述 <pre>生成公私钥对 公钥给别人拿去加密传输数据，私钥留个自己用来解密</pre>
     * @作者    yw
     * @创建时间 2021年4月13日 下午1:57:07
     * @throws NoSuchAlgorithmException
     */
    public static SM2KeyPair genPUPRkey() throws NoSuchAlgorithmException {

        //生成密钥对
        ECKeyPairGenerator keyPairGenerator = new ECKeyPairGenerator();
        keyPairGenerator.init(new ECKeyGenerationParameters(domainParameters, SecureRandom.getInstance("SHA1PRNG")));
        AsymmetricCipherKeyPair asymmetricCipherKeyPair = keyPairGenerator.generateKeyPair();

        //私钥，16进制格式，自己保存，格式如80cfeb110007a9a11295b9289c4b889063f35fe66e54d53fdbb5b8aa335d665a
        BigInteger privatekey = ((ECPrivateKeyParameters) asymmetricCipherKeyPair.getPrivate()).getD();
        String privateKeyHex = privatekey.toString(16);
        System.out.println("私钥：" + privateKeyHex);

        //公钥，16进制格式，发给前端，格式如0467e8c00b1fc2049aa006e75a7216eaf1fb42347b664ea63897917f1281427fa22254d39a3f5fd38e6773edc5eddc074dae27bfbe82d13094bfcbe6b344e89ee9
        ECPoint ecPoint = ((ECPublicKeyParameters) asymmetricCipherKeyPair.getPublic()).getQ();
        String publicKeyHex = Hex.toHexString(ecPoint.getEncoded(false));
        System.out.println("公钥：" + publicKeyHex);
        SM2KeyPair sm2KeyPair = new SM2KeyPair(publicKeyHex,privateKeyHex);
        return sm2KeyPair;
    }

    /**
     * SM2加密算法
     * @param publicKeyStr     公钥
     * @param data          明文数据
     * @return
     */
    public static String encrypt(String publicKeyStr, String data){
        Security.addProvider(new BouncyCastleProvider());
        PublicKey publicKey = null;
        try {
            byte[] keyBytes;
            keyBytes = (new BASE64Decoder()).decodeBuffer(publicKeyStr);
            KeyFactory keyFactory = KeyFactory.getInstance("EC", BouncyCastleProvider.PROVIDER_NAME);
            X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);
            publicKey = keyFactory.generatePublic(keySpec);
        } catch (Exception e) {
            e.printStackTrace();
        }
        ECPublicKeyParameters ecPublicKeyParameters = null;
        if (publicKey instanceof BCECPublicKey) {
            BCECPublicKey bcecPublicKey = (BCECPublicKey) publicKey;
            ECParameterSpec ecParameterSpec = bcecPublicKey.getParameters();
            ECDomainParameters ecDomainParameters = new ECDomainParameters(ecParameterSpec.getCurve(),
                    ecParameterSpec.getG(), ecParameterSpec.getN());
            ecPublicKeyParameters = new ECPublicKeyParameters(bcecPublicKey.getQ(), ecDomainParameters);
        }
        SM2Engine sm2Engine = new SM2Engine();
        sm2Engine.init(true, new ParametersWithRandom(ecPublicKeyParameters, new SecureRandom()));
        byte[] arrayOfBytes = null;
        try {
            byte[] in = data.getBytes("utf-8");
            arrayOfBytes = sm2Engine.processBlock(in,0, in.length);
        } catch (Exception e) {
            System.out.println("SM2加密时出现异常:");
        }
        String s = new String(arrayOfBytes);
        return  s;
    }

    /**
     * 解密
     * @方法名称 decrypt
     * @功能描述 <pre></pre>
     * @作者    yw
     * @创建时间 2021年4月13日 下午2:09:59
     * @param ciphertext
     * @param privatekey
     * @throws InvalidCipherTextException
     */
    public static String decrypt(String ciphertext, String privatekey) throws InvalidCipherTextException {
        String cipherData = ciphertext;//JS加密产生的密文
        byte[] cipherDataByte = Hex.decode(cipherData);//格式转换

        BigInteger privateKeyD = new BigInteger(privatekey, 16);//私钥Hex，还原私钥
        ECPrivateKeyParameters privateKeyParameters = new ECPrivateKeyParameters(privateKeyD, domainParameters);

        //用私钥解密
        SM2Engine sm2Engine = new SM2Engine();
        sm2Engine.init(false, privateKeyParameters);

        //processBlock得到Base64格式，记得解码
        byte[] arrayOfBytes = Base64.getDecoder().decode(sm2Engine.processBlock(cipherDataByte, 0, cipherDataByte.length));

        //得到明文：SM2 Encryption Test
        String mtext = new String(arrayOfBytes);
        System.out.println(mtext);
        return mtext;
    }


    /**
     *
     * @方法名称 verification
     * @功能描述 <pre>js的密文校验</pre>
     * @作者    yw
     * @创建时间 2021年4月13日 下午2:13:07
     * @param ciphertext
     * @return
     */
    public static String verification(String ciphertext) {
        String mtext = "";//明文
        try {
            mtext = decrypt(ciphertext, privateKey);
            System.out.println("mtext:" + mtext);
        } catch (InvalidCipherTextException e) {
            e.printStackTrace();
        }
        return mtext ;
    }

    public static void generateKey(){
       try {
            genPUPRkey();
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        }
    }

    public static void main(String args[]) {
        // 前端加密的密文
        String text = "0458059fd17d7b73d22ac944e33072fdb145ebc09228f4e2659bc4557c673296fcec52930643f9974f374b24a78ef36027b6a0ab07c9008ffb88d8b2d759ea060e93b33a4d5648f40bdc9405c95d45a2d3b9020e1a285a0ab6f9c9dc32a891b432937ee2ed74cb103c87c6ae00745aeb1a31441032f1bde7e316cc7c6d51dbe8300c97a745e01970b5ef5dd255";
        verification(text);

        //生成公钥和密钥
        //generateKey();
    }

}